#include "QtUI/ProjectTreeView.qth"
#include "QtUI/ProjectTreeView.moh"
#include "QtUI/PackageCommands.qth"
#include "QtUI/UICommandBase.qth"
#include "Ctrl/ControlCenter.hpp"
#include "Model/Project.hpp"
#include "Model/Package.hpp"
#include "Model/Requirement.hpp"
#include <QtGui/QTreeWidgetItem>
#include <QtGui/QMenu>

Q_DECLARE_METATYPE( Model::Package*)
Q_DECLARE_METATYPE( Model::CaseEntity*)

namespace 
{

template< typename T>
QTreeWidgetItem* findItemByData( QTreeWidgetItem* pParent, const T& data )
{
	assert( NULL != pParent);
	QTreeWidgetItem* result = NULL;
	int childCount = pParent->childCount();
	for( int childI = 0; childI < childCount && NULL == result; ++childI)
	{
		QTreeWidgetItem* pCurChildItem = pParent->child( childI);
		QVariant itemData = pCurChildItem->data( 0, Qt::UserRole);
		T curVal = itemData.value<T>();
		if( data == curVal)
		{
			return pCurChildItem;
		}
		else
		{
			result = findItemByData( pCurChildItem, data);
		}
	}
	return result;
}

} // namespace

namespace QtUI
{

ProjectTreeView::ProjectTreeView( Ctrl::ControlCenter& ctrler, QWidget* pParent /* = NULL*/)
	: QtProjectViewBase( ctrler.prjController())
	, QtPackageViewBase( ctrler.packageController())
	, QtSelectionViewBase( ctrler.selectionModel())
	, QTreeWidget( pParent)
	, selectionJustChanged_( false)
{
	this->setContextMenuPolicy( Qt::CustomContextMenu);
	this->setHeaderHidden( true);
	this->setSelectionBehavior( QAbstractItemView::SelectRows);
	this->setSelectionMode( QAbstractItemView::ExtendedSelection);

	connect( 
		this, SIGNAL( currentItemChanged( QTreeWidgetItem*, QTreeWidgetItem*)), 
		this, SLOT( handleCurrentItemChanged( QTreeWidgetItem*, QTreeWidgetItem*)));
	connect(
		this, SIGNAL( customContextMenuRequested( const QPoint&)), this, SLOT(handleCustomContextMenuRequested(const QPoint& )));
}

// ---------------------------------------------------------------------------------------------------------------------

QTreeWidgetItem* ProjectTreeView::createPackageItem( Model::Package* pPackage)
{
	QTreeWidgetItem* pNewItem = new QTreeWidgetItem();

	// Set package data to the item
	pNewItem->setText( 0, QString::fromStdString( pPackage->name()));
	QVariant data;
	Model::CaseEntity* pBoundEntity = dynamic_cast< Model::CaseEntity*>( pPackage);
	data.setValue( pBoundEntity);
	pNewItem->setData( 0, Qt::UserRole, data);

	// Now, set the root requirement packages, if any.
	Model::Package::RequirementIterator rqI = pPackage->requirementsBegin(),
		rqEI = pPackage->requirementsEnd();
	while( rqI != rqEI)
	{
		pNewItem->addChild( this->createRootRequirementItem( *rqI));
		++rqI;
	}
	return pNewItem;
}

// ---------------------------------------------------------------------------------------------------------------------

QTreeWidgetItem* ProjectTreeView::findPackageItem( Model::Package* pPackage)
{
	assert( NULL != pPackage);
	QTreeWidgetItem* pTopLevelItem = this->topLevelItem( 0);
	return findItemByData( pTopLevelItem, dynamic_cast< Model::CaseEntity*>( pPackage));
}

// ---------------------------------------------------------------------------------------------------------------------

QTreeWidgetItem* ProjectTreeView::findRequirementItem( Model::Requirement* pRequirement)
{
	assert( NULL != pRequirement);
	QTreeWidgetItem* pTopLevelItem = this->topLevelItem( 0);
	return findItemByData( pTopLevelItem, dynamic_cast< Model::CaseEntity*>( pRequirement));
}

// ---------------------------------------------------------------------------------------------------------------------

QTreeWidgetItem* ProjectTreeView::createRootRequirementItem( Model::Requirement* pRequirement)
{
	assert( NULL != pRequirement);
	assert( !pRequirement->hasParent());

	QTreeWidgetItem* pResult = new QTreeWidgetItem;
	pResult->setText( 0, QString::fromStdString( pRequirement->name()));
	QVariant data;
	Model::CaseEntity* pBoundEntity = dynamic_cast< Model::CaseEntity*>( pRequirement);
	data.setValue( pBoundEntity);
	pResult->setData( 0, Qt::UserRole, data);
	return pResult;
}

// ---------------------------------------------------------------------------------------------------------------------


void ProjectTreeView::handleProjectChanged( Model::Project* pNewPrj)
{
	this->clear();
	QTreeWidgetItem* pProjectItem = new QTreeWidgetItem();
	pProjectItem->setText(
		0, 
		(pNewPrj == NULL) ? 
			tr("No Project", "No Project Surrogate Name for Project Root item in Project Tree View")
			: QString::fromStdString( pNewPrj->name()));
	pProjectItem->setToolTip( 
		0,
		(pNewPrj == NULL) ? "" : QString::fromStdString( pNewPrj->description()));
	this->addTopLevelItem( pProjectItem);
	if( NULL != pNewPrj)
	{
		this->populatePackageItems( pNewPrj);
	}
}

// ---------------------------------------------------------------------------------------------------------------------

void ProjectTreeView::handleProjectDescriptionChanged( const Model::Project& changedProject)
{
	this->topLevelItem(0)->setToolTip( 0, QString::fromStdString( changedProject.description()));
}

// ---------------------------------------------------------------------------------------------------------------------

void ProjectTreeView::handlePackageAdded( Model::Package* pParent, Model::Package* pNewPackage)
{
	QTreeWidgetItem* pParentItem = this->findPackageItem( pParent);
	if( NULL == pParentItem)
	{
		pParentItem = this->topLevelItem(0);
	}
	QTreeWidgetItem* pNewItem = this->createPackageItem( pNewPackage);
	pParentItem->addChild( pNewItem);
}

// ---------------------------------------------------------------------------------------------------------------------

void ProjectTreeView::handlePackageRemoved( Model::Package* pParent, Model::Package* pRemovedPackage)
{
	QTreeWidgetItem* pItemToRemove = this->findPackageItem( pRemovedPackage);
	assert( NULL != pItemToRemove);

	delete pItemToRemove;
}

// ---------------------------------------------------------------------------------------------------------------------

void ProjectTreeView::populatePackageItems( Model::Project* pNewPrj)
{
	using namespace Model;
	Package& root = pNewPrj->rootPackage();
	this->populateChildPackageItems( root, this->topLevelItem(0));
}

// ---------------------------------------------------------------------------------------------------------------------

void ProjectTreeView::populateChildPackageItems( Model::Package& parent, QTreeWidgetItem* pParentItem)
{
	using namespace Model;
	Package::PackageIterator childI = parent.childPackagesBegin(),
		endI = parent.childPackagesEnd();
	while( childI != endI)
	{
		QTreeWidgetItem* pChildItem = this->createPackageItem( *childI);
		pParentItem->addChild( pChildItem);
		this->populateChildPackageItems( **childI, pChildItem);
		++childI;
	}
}

// ---------------------------------------------------------------------------------------------------------------------

void ProjectTreeView::handleCurrentItemChanged( QTreeWidgetItem* pCurrent, QTreeWidgetItem* pPrevious)
{
	Model::Package* pCurrentPackage = this->getBoundPackage( pCurrent);
	rPackageController_.setCurrentPackage( pCurrentPackage);
}

// ---------------------------------------------------------------------------------------------------------------------

Model::Package* ProjectTreeView::getBoundPackage( QTreeWidgetItem* pTreeItem)
{
	if( NULL == pTreeItem)
		return NULL;

	QVariant data = pTreeItem->data( 0, Qt::UserRole);
	Model::Package* pResult = dynamic_cast< Model::Package*>( data.value< Model::CaseEntity*>());
	return pResult;
}

// ---------------------------------------------------------------------------------------------------------------------

Model::CaseEntity* ProjectTreeView::getBoundEntity( QTreeWidgetItem* pTreeItem)
{
	if( NULL == pTreeItem) return NULL;

	QVariant data = pTreeItem->data( 0, Qt::UserRole);
	Model::CaseEntity* pResult = data.value< Model::CaseEntity*>();
	return pResult;
}

// ---------------------------------------------------------------------------------------------------------------------

void ProjectTreeView::handleCustomContextMenuRequested ( const QPoint & pos )
{
	QTreeWidgetItem* pContextedItem = this->itemAt( pos);
	QTreeWidgetItem* pRootItem = this->topLevelItem(0);
	if( NULL == pContextedItem)
	{
		return;
	}

	Model::Package* pCurrentPackage = this->getBoundPackage( pContextedItem);
	if( pCurrentPackage != rPackageController_.currentPackage())
	{
		rPackageController_.setCurrentPackage( pCurrentPackage);
		this->setCurrentItem( pContextedItem);
	}

	QMenu contextMenu;
	bool canDisplayContextMenu = false;
	
	if( NULL != pCurrentPackage || pRootItem == pContextedItem)
	{
		// Make package related context menu
		contextMenu.addAction( PackageCommands::getNewPackageCommand().boundAction());
		contextMenu.addAction( PackageCommands::getNewRootRequirementCommand().boundAction());
		canDisplayContextMenu = true;
	}
	if( canDisplayContextMenu)
		contextMenu.exec( this->mapToGlobal( pos));
}

// ---------------------------------------------------------------------------------------------------------------------

void ProjectTreeView::handleSelectionChanged( const Ctrl::SelectionModel& changedSelection)
{
	if( !selectionJustChanged_)
	{
		selectionJustChanged_ = true;
		QTreeWidgetItemIterator itemI( this);
		while( *itemI)
		{
			QTreeWidgetItem* pCurItem = *itemI;
			Model::CaseEntity* pCurEntity = this->getBoundEntity( pCurItem);
			pCurItem->setSelected( changedSelection.isSelected( pCurEntity));
			++itemI;
		}
	}
}

// ---------------------------------------------------------------------------------------------------------------------

void ProjectTreeView::selectionChanged ( const QItemSelection & current, const QItemSelection & previous)
{
//	QTreeWidget::selectionChanged( current, previous);
	if( !selectionJustChanged_)
	{
		selectionJustChanged_ = true;
		std::list< Model::CaseEntity*> newSelection;
		this->getSelectedCaseEntities( newSelection);
		rSelectionModel_.replaceSelection( newSelection);
	}
}

// ---------------------------------------------------------------------------------------------------------------------

void ProjectTreeView::getSelectedCaseEntities( std::list< Model::CaseEntity*>& resultedSelection)
{
	QTreeWidgetItemIterator widgetI( this, QTreeWidgetItemIterator::Selected);
	while( *widgetI)
	{
		QTreeWidgetItem* pCurItem = *widgetI;
		Model::CaseEntity* pBoundEntity = this->getBoundEntity( pCurItem);
		if( NULL != pBoundEntity)
		{
			resultedSelection.push_back( pBoundEntity);
		}
		++widgetI;
	}
}

// ---------------------------------------------------------------------------------------------------------------------

void ProjectTreeView::handleRootRequirementAdded( Model::Package* pParent, Model::Requirement* pAddedRequirement)
{
	QTreeWidgetItem* pPackageItem = this->findPackageItem( pParent);
	QTreeWidgetItem* pRootRqItem = this->createRootRequirementItem( pAddedRequirement);
	pPackageItem->addChild( pRootRqItem);
	this->clearSelection();
	pRootRqItem->setSelected( true);
	pPackageItem->setExpanded( true);
}

// ---------------------------------------------------------------------------------------------------------------------

void ProjectTreeView::handleRootRequirementRemoved( Model::Package* pParent, Model::Requirement* pRemovedRequirement)
{
	QTreeWidgetItem* pRqItem = this->findRequirementItem( pRemovedRequirement);
	assert( NULL != pRqItem);
	delete pRqItem;
}

} // namespace QtUI
